import os
from openai import OpenAI
from llama_index.core import StorageContext,load_index_from_storage,Settings,VectorStoreIndex
from llama_index.embeddings.dashscope import (
    DashScopeEmbedding,
    DashScopeTextEmbeddingModels,
    DashScopeTextEmbeddingType,
)
from llama_index.postprocessor.dashscope_rerank import DashScopeRerank
from create_kb import *
from milvus_kb import create_tmp_milvus_kb, get_milvus_collections, create_milvus_vector_store, normalize_collection_name
from ThshareTool import tushare_tool
import json
from datetime import datetime, timedelta

DB_PATH = "VectorStore"
TMP_NAME = "tmp_abcd"
EMBED_MODEL = DashScopeEmbedding(
    model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2,
    text_type=DashScopeTextEmbeddingType.TEXT_TYPE_DOCUMENT,
)
# 若使用本地嵌入模型，请取消以下注释：
# from langchain_community.embeddings import ModelScopeEmbeddings
# from llama_index.embeddings.langchain import LangchainEmbedding
# embeddings = ModelScopeEmbeddings(model_id="modelscope/iic/nlp_gte_sentence-embedding_chinese-large")
# EMBED_MODEL = LangchainEmbedding(embeddings)

# 设置嵌入模型
Settings.embed_model = EMBED_MODEL

# 定义股票查询相关的函数
def get_stock_functions():
    """返回股票查询相关的函数定义"""
    return [
        {
            "type": "function",
            "function": {
                "name": "get_stock_historical_data",
                "description": "获取股票的历史行情数据，包括开盘价、收盘价、最高价、最低价、成交量等",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "symbol": {
                            "type": "string",
                            "description": "股票代码，例如：000001.SZ（平安银行）、600000.SH（浦发银行）"
                        },
                        "start_date": {
                            "type": "string",
                            "description": "起始日期，格式为YYYYMMDD，例如：20240101"
                        },
                        "end_date": {
                            "type": "string",
                            "description": "结束日期，格式为YYYYMMDD，例如：20241231"
                        }
                    },
                    "required": ["symbol", "start_date", "end_date"]
                }
            }
        }
    ]

# 股票查询函数实现
def get_stock_historical_data(symbol, start_date, end_date):
    """获取股票历史数据"""
    try:
        data = tushare_tool.get_stock_data(symbol, start_date, end_date)
        if data.empty:
            return json.dumps({"error": "未找到数据"}, ensure_ascii=False, indent=2)
        # 只返回最近10条数据，避免数据过多
        result = data.head(10).to_dict('records')
        return json.dumps(result, ensure_ascii=False, indent=2)
    except Exception as e:
        return json.dumps({"error": f"获取历史数据失败: {str(e)}"}, ensure_ascii=False, indent=2)

# 函数映射字典
function_map = {
    "get_stock_historical_data": get_stock_historical_data
}

def get_model_response(multi_modal_input,history,model,temperature,max_tokens,history_round,db_name,similarity_threshold,chunk_cnt):
    # 辅助函数：安全地设置history响应内容
    def safe_set_history_response(response_text):
        if history and len(history) > 0:
            # 检查是否已经有助手消息
            if history and len(history) > 0 and history[-1].get('role') == 'assistant':
                # 更新最后一条助手消息
                history[-1]['content'] = response_text
            else:
                # 添加新的助手消息
                assistant_msg = {"role": "assistant", "content": response_text}
                history.append(assistant_msg)
        else:
            print("警告：history结构不正确，无法设置响应内容")
    
    # 检查history是否为空
    if not history or len(history) == 0:
        print("警告：history列表为空，无法获取用户输入")
        return history, ""
    
    # 获取最后一个用户消息
    last_user_msg = None
    for msg in reversed(history):
        if msg.get('role') == 'user':
            last_user_msg = msg
            break
    
    if not last_user_msg:
        print("警告：未找到用户消息")
        return history, ""
    
    prompt = last_user_msg.get('content', '')
    if not prompt:
        print("警告：用户消息内容为空")
        return history, ""
    
    tmp_files = multi_modal_input['files']
    
    # 修复db_name为None的问题
    if db_name is None:
        db_name = "default"
    
    # 检查是否有新上传的文件需要创建临时知识库
    if tmp_files:
        # 优先尝试创建Milvus临时知识库
        try:
            create_tmp_milvus_kb(tmp_files)
            db_name = TMP_NAME
        except Exception as e:
            print(f"创建Milvus临时知识库失败，回退到本地存储: {e}")
            create_tmp_kb(tmp_files)
            db_name = TMP_NAME
    # 如果没有新文件但有临时知识库存在，且用户没有选择其他知识库，则使用临时知识库
    elif os.path.exists(os.path.join("File",TMP_NAME)) and (db_name == "default" or db_name is None):
        db_name = TMP_NAME
    # 否则使用用户选择的知识库（db_name保持不变）
    
    # 获取index
    
    # 检查是否需要股票查询功能
    stock_keywords = ['股票', '股价', '行情', '涨跌', '代码', '历史', '数据', '平安', '银行', '科技', '上证', '深证']
    is_stock_query = any(keyword in prompt for keyword in stock_keywords)
    
    # 首先尝试从知识库检索信息（无论是否为股票查询）
    chunk_text = ""
    chunk_show = ""
    
    # 检查是否有知识库可用
    has_knowledge_base = False
    
    # 检查是否有新上传的文件
    if tmp_files:
        has_knowledge_base = True
    
    # 检查用户选择的知识库是否存在
    if db_name and db_name != "default":
        # 检查是否为Milvus知识库
        try:
            milvus_collections = get_milvus_collections()
            normalized_db_name = normalize_collection_name(db_name)
            is_milvus_db = db_name in milvus_collections or normalized_db_name in milvus_collections
            
            if is_milvus_db:
                has_knowledge_base = True
            else:
                # 检查本地知识库是否存在
                db_path = os.path.join(DB_PATH, db_name)
                if os.path.exists(db_path):
                    has_knowledge_base = True
        except Exception as e:
            print(f"检查知识库失败: {e}")
    
    # 如果没有选择知识库，检查是否有临时知识库
    if not has_knowledge_base:
        # 检查是否已存在临时知识库
        if os.path.exists(os.path.join("File", TMP_NAME)):
            has_knowledge_base = True
            db_name = TMP_NAME
        
        # 检查Milvus中是否有临时集合
        try:
            milvus_collections = get_milvus_collections()
            if TMP_NAME in milvus_collections:
                has_knowledge_base = True
                db_name = TMP_NAME
        except Exception as e:
            print(f"检查Milvus集合失败: {e}")
    
    # 如果有知识库，先尝试检索
    if has_knowledge_base:
        try:
            print(f"正在从知识库 '{db_name}' 检索信息...")
            
            # 首先检查是否为Milvus知识库
            milvus_collections = get_milvus_collections()
            # 同时检查原始名称和规范化后的名称
            normalized_db_name = normalize_collection_name(db_name)
            is_milvus_db = db_name in milvus_collections or normalized_db_name in milvus_collections
            
            if is_milvus_db:
                # 对于临时知识库，直接使用原生pymilvus查询
                if db_name == TMP_NAME:
                    try:
                        from pymilvus import connections, Collection, utility
                        
                        # 连接到Milvus
                        connections.connect(
                            alias="default",
                            host=os.getenv("MILVUS_HOST", "localhost"),
                            port=os.getenv("MILVUS_PORT", "19530"),
                            user=os.getenv("MILVUS_USER", ""),
                            password=os.getenv("MILVUS_PASSWORD", "")
                        )
                        
                        # 获取集合
                        collection = Collection(db_name)
                        collection.load()
                        
                        # 生成查询向量
                        query_embedding = EMBED_MODEL.get_text_embedding(prompt)
                        
                        # 执行向量搜索
                        search_params = {
                            "metric_type": "COSINE",
                            "params": {"nprobe": 10}
                        }
                        
                        results = collection.search(
                            data=[query_embedding],
                            anns_field="embedding",
                            param=search_params,
                            limit=20,
                            output_fields=["text"]
                        )
                        
                        connections.disconnect("default")
                        
                        # 处理搜索结果
                        for i, result in enumerate(results[0]):
                            score = result.score
                            if score >= similarity_threshold:
                                text = result.entity.get('text', '')
                                chunk_text = chunk_text + f"## {i+1}:\n {text}\n"
                                chunk_show = chunk_show + f"## {i+1}:\n {text}\nscore: {round(score,2)}\n"
                        
                    except Exception as e:
                        print(f"临时Milvus知识库查询失败: {e}")
                else:
                    # 对于非临时知识库，也使用原生pymilvus查询
                    try:
                        from pymilvus import connections, Collection, utility
                        
                        # 连接到Milvus
                        connections.connect(
                            alias="default",
                            host=os.getenv("MILVUS_HOST", "localhost"),
                            port=os.getenv("MILVUS_PORT", "19530"),
                            user=os.getenv("MILVUS_USER", ""),
                            password=os.getenv("MILVUS_PASSWORD", "")
                        )
                        
                        # 尝试获取集合（先尝试原始名称，再尝试规范化名称）
                        collection = None
                        normalized_db_name = normalize_collection_name(db_name)
                        
                        if utility.has_collection(db_name):
                            collection = Collection(db_name)
                        elif utility.has_collection(normalized_db_name):
                            collection = Collection(normalized_db_name)
                        else:
                            print(f"Milvus知识库 {db_name} 或 {normalized_db_name} 不存在")
                            connections.disconnect("default")
                            return history, chunk_show
                        
                        collection.load()
                        
                        # 生成查询向量
                        query_embedding = EMBED_MODEL.get_text_embedding(prompt)
                        
                        # 执行向量搜索
                        search_params = {
                            "metric_type": "COSINE",
                            "params": {"nprobe": 10}
                        }
                        
                        results = collection.search(
                            data=[query_embedding],
                            anns_field="embedding",
                            param=search_params,
                            limit=20,
                            output_fields=["text"]
                        )
                        
                        connections.disconnect("default")
                        
                        # 处理搜索结果
                        for i, result in enumerate(results[0]):
                            score = result.score
                            if score >= similarity_threshold:
                                text = result.entity.get('text', '')
                                chunk_text = chunk_text + f"## {i+1}:\n {text}\n"
                                chunk_show = chunk_show + f"## {i+1}:\n {text}\nscore: {round(score,2)}\n"
                        
                    except Exception as e:
                        print(f"Milvus知识库 {db_name} 查询失败: {e}")
                        import traceback
                        traceback.print_exc()
            else:
                # 检查本地知识库是否存在
                db_path = os.path.join(DB_PATH, db_name)
                if os.path.exists(db_path):
                    dashscope_rerank = DashScopeRerank(top_n=chunk_cnt,return_documents=True)
                    storage_context = StorageContext.from_defaults(
                        persist_dir=db_path
                    )
                    index = load_index_from_storage(storage_context)
                    retriever_engine = index.as_retriever(
                        similarity_top_k=20,
                    )
                    # 获取chunk
                    retrieve_chunk = retriever_engine.retrieve(prompt)
                    try:
                        results = dashscope_rerank.postprocess_nodes(retrieve_chunk, query_str=prompt)
                    except:
                        results = retrieve_chunk[:chunk_cnt]
                    for i in range(len(results)):
                        if results[i].score >= similarity_threshold:
                            chunk_text = chunk_text + f"## {i+1}:\n {results[i].text}\n"
                            chunk_show = chunk_show + f"## {i+1}:\n {results[i].text}\nscore: {round(results[i].score,2)}\n"
        except Exception as e:
            print(f"知识库检索异常信息：{e}")
    
    # 构建prompt模板
    if chunk_text:
        # 如果有知识库内容，优先使用知识库内容
        if is_stock_query:
            # 股票查询 + 知识库内容
            prompt_template = f"请参考以下内容：{chunk_text}，回答用户关于股票操作的问题：{prompt}。如果参考内容中有相关信息，请基于内容回答；如果没有相关信息，请说明无法从文档中找到相关信息。"
        else:
            # 普通查询 + 知识库内容
            prompt_template = f"请参考以下内容：{chunk_text}，以合适的语气回答用户的问题：{prompt}。如果参考内容中有图片链接也请直接返回。"
    else:
        # 如果没有知识库内容
        if is_stock_query:
            # 股票查询，但没有知识库内容，使用function calling
            prompt_template = prompt
        else:
            # 普通查询，但没有知识库内容
            prompt_template = prompt
    
    # 检查history结构是否正确
    if history and len(history) > 0 and len(history[-1]) >= 2:
        safe_set_history_response("")
    else:
        print("警告：history结构不正确，无法设置响应内容")
        return history, chunk_show
    
    client = OpenAI(
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    )                
    system_message = {'role': 'system', 'content': '''You are a helpful assistant specialized in stock market queries and document-based Q&A. 

When users ask questions, you should:

1. **If reference content is provided**: First analyze the reference content to see if it contains relevant information to answer the user's question. If it does, provide a comprehensive answer based on the reference content.

2. **For stock data queries without reference content**: 
   - Extract the stock symbol from user input. Here are common stock mappings:
     * 平安银行 = 000001.SZ
     * 浦发银行 = 600000.SH
     * 中国石油 = 601857.SH
     * 中国石化 = 600028.SH
     * 中国银行 = 601988.SH
     * 工商银行 = 601398.SH
     * 建设银行 = 601939.SH
     * 农业银行 = 601288.SH
     * 招商银行 = 600036.SH
     * 中科曙光 = 603019.SH
     * 贵州茅台 = 600519.SH
     * 五粮液 = 000858.SZ
     * 比亚迪 = 002594.SZ
     * 宁德时代 = 300750.SZ
     * 腾讯控股 = 00700.HK (港股)
     * 阿里巴巴 = 09988.HK (港股)
   - Extract the date range from user input
   - **IMPORTANT: You MUST use the get_stock_historical_data function call, NOT return JSON directly**
   - Use the get_stock_historical_data function with EXACTLY these parameters:
     - symbol: stock code in format like "000001.SZ" or "600000.SH"
     - start_date: start date in YYYYMMDD format (e.g., "20240101")
     - end_date: end date in YYYYMMDD format (e.g., "20241231")

3. **For questions about stock operations or trading records**:
   - If reference content contains trading records or operation logs, analyze and present the information clearly
   - If no relevant information is found in the reference content, clearly state that the information is not available in the provided documents

CRITICAL REQUIREMENTS:
- Always prioritize reference content when available
- **ALWAYS use function calls for stock queries, NEVER return JSON directly**
- Generate COMPLETE and VALID JSON in ONE response for function calls
- Do NOT truncate or leave incomplete JSON
- Keep JSON compact and simple
- For date ranges like "2025年1月到3月", convert to "20250101" to "20250331"
- When you receive stock data results, analyze and present them in a user-friendly format
- Do NOT repeat the function parameters in your response
- Instead, provide insights about the stock performance, trends, and key metrics
- If user asks for specific analysis (like highest/lowest prices), provide detailed analysis with dates and values

IMPORTANT: Always generate complete JSON with proper quotes and structure:
{"symbol":"000001.SZ","start_date":"20250101","end_date":"20250331"}

Example function calls:
- User: "查看600000.SH从20240101到20240131的历史行情"
- Function: {"symbol":"600000.SH","start_date":"20240101","end_date":"20240131"}

- User: "获取平安银行2025年1月到3月的股价数据"
- Function: {"symbol":"000001.SZ","start_date":"20250101","end_date":"20250331"}

- User: "获取中国石油2025年1月到3月的股价数据"
- Function: {"symbol":"601857.SH","start_date":"20250101","end_date":"20250331"}

When analyzing stock data, focus on:
- Price trends (opening, closing, high, low)
- Volume and trading activity
- Price changes and percentage changes
- Key trading days and patterns
- Highest and lowest prices with their dates
- Overall performance summary

For general questions not related to stocks, provide helpful answers based on available knowledge.
'''}
    messages = []
    # 从history中提取最近的消息用于上下文
    recent_messages = history[-history_round*2:] if len(history) > history_round*2 else history
    
    for msg in recent_messages:
        if msg.get('role') in ['user', 'assistant']:
            messages.append({
                'role': msg['role'],
                'content': msg['content']
            })
    
    messages.append({'role': 'user', 'content': prompt_template})
    messages = [system_message] + messages
    
    # 如果是股票查询，添加function calling支持
    if is_stock_query:
        print(f"🔍 检测到股票查询: {prompt}")
        print(f"📝 使用提示词模板: {prompt_template[:100]}...")
        
        completion = client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens,
            tools=get_stock_functions(),
            tool_choice="auto",
            stream=True
        )
    else:
        completion = client.chat.completions.create(
            model=model,
            messages=messages,
            temperature=temperature,
            max_tokens=max_tokens,
            stream=True
        )
    
    assistant_response = ""
    tool_calls = []
    current_tool_call = None
    
    print(f"🔄 开始处理流式响应...")
    
    # 改进：等待流式响应完全结束后再处理
    for chunk in completion:
        if chunk.choices[0].delta.content:
            assistant_response += chunk.choices[0].delta.content
            # 安全检查history结构
            safe_set_history_response(assistant_response)
            yield history, chunk_show
        
        # 处理function calling - 改进参数收集逻辑
        if chunk.choices[0].delta.tool_calls:
            print(f"🔧 检测到tool call chunk")
            for tool_call in chunk.choices[0].delta.tool_calls:
                # 如果是新的tool call
                if current_tool_call is None or current_tool_call['id'] != tool_call.id:
                    current_tool_call = {
                        'id': tool_call.id,
                        'function': {
                            'name': tool_call.function.name,
                            'arguments': tool_call.function.arguments or ""
                        }
                    }
                    tool_calls.append(current_tool_call)
                    print(f"🆕 新建tool call: {tool_call.function.name}")
                else:
                    # 继续收集现有tool call的参数
                    if tool_call.function.arguments:
                        current_tool_call['function']['arguments'] += tool_call.function.arguments
                        print(f"📝 追加参数: {tool_call.function.arguments}")
    
    print(f"📊 流式响应结束，收集到 {len(tool_calls)} 个tool calls")
    print(f"📝 助手回复: {assistant_response[:100]}...")
    
    # 合并相同ID的tool call参数 - 改进合并逻辑
    merged_tool_calls = {}
    for tool_call in tool_calls:
        tool_id = tool_call['id']
        if tool_id not in merged_tool_calls:
            merged_tool_calls[tool_id] = {
                'id': tool_id,
                'function': {
                    'name': tool_call['function']['name'],
                    'arguments': tool_call['function']['arguments']
                }
            }
        else:
            # 合并参数 - 确保参数完整性
            existing_args = merged_tool_calls[tool_id]['function']['arguments']
            new_args = tool_call['function']['arguments']
            merged_tool_calls[tool_id]['function']['arguments'] = existing_args + new_args
    
    # 使用合并后的tool calls
    tool_calls = list(merged_tool_calls.values())
    
    # 执行function calls - 确保完整收集后再处理
    if tool_calls:
        print(f"🔧 开始处理 {len(tool_calls)} 个tool calls")
        for i, tool_call in enumerate(tool_calls):
            print(f"🔧 处理第 {i+1} 个tool call")
            try:
                function_name = tool_call['function']['name']
                arguments_str = tool_call['function']['arguments']
                
                print(f"   📝 函数名: {function_name}")
                print(f"   📝 参数字符串: '{arguments_str}'")
                
                # 检查参数是否完整 - 改进检查逻辑
                if not arguments_str:
                    print("❌ 参数字符串为空")
                    error_response = "抱歉，无法解析参数。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                    safe_set_history_response(error_response)
                    yield history, chunk_show
                    return
                
                # 检查JSON是否完整 - 更严格的检查
                arguments_str = arguments_str.strip()
                if not arguments_str.startswith('{') or not arguments_str.endswith('}'):
                    print(f"JSON格式不完整: '{arguments_str}'")
                    print("尝试使用非流式处理重新获取参数")
                    # 如果流式处理的参数不完整，尝试非流式处理
                    try:
                        non_stream_completion = client.chat.completions.create(
                            model=model,
                            messages=messages,
                            temperature=temperature,
                            max_tokens=max_tokens,
                            tools=get_stock_functions(),
                            tool_choice="auto",
                            stream=False
                        )
                        
                        if non_stream_completion.choices[0].message.tool_calls:
                            non_stream_tool_call = non_stream_completion.choices[0].message.tool_calls[0]
                            function_name = non_stream_tool_call.function.name
                            arguments_str = non_stream_tool_call.function.arguments
                            print(f"非流式处理成功，参数: {arguments_str}")
                        else:
                            print("非流式处理也未生成tool_calls，检查是否直接返回了JSON")
                            # 检查是否直接返回了JSON
                            content = non_stream_completion.choices[0].message.content
                            if content and ('{"symbol"' in content or '"symbol"' in content):
                                print(f"检测到直接返回的JSON: {content}")
                                # 尝试从内容中提取JSON
                                import re
                                json_match = re.search(r'\{[^}]+\}', content)
                                if json_match:
                                    arguments_str = json_match.group(0)
                                    print(f"提取的JSON: {arguments_str}")
                                else:
                                    error_response = "抱歉，无法获取完整参数。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                                    safe_set_history_response(error_response)
                                    yield history, chunk_show
                                    return
                            else:
                                error_response = "抱歉，无法获取完整参数。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                                safe_set_history_response(error_response)
                                yield history, chunk_show
                                return
                    except Exception as e:
                        print(f"非流式处理失败: {e}")
                        error_response = "抱歉，参数格式不完整。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                        safe_set_history_response(error_response)
                        yield history, chunk_show
                        return
                
                # 修复JSON解析错误
                arguments = {}
                try:
                    # 清理JSON字符串
                    arguments_str = arguments_str.strip()
                    # 移除可能的BOM和特殊字符
                    arguments_str = arguments_str.replace('\ufeff', '')
                    # 尝试解析JSON
                    arguments = json.loads(arguments_str)
                except json.JSONDecodeError as json_error:
                    # 尝试修复常见的JSON格式问题
                    try:
                        # 尝试修复缺少引号的问题
                        import re
                        # 修复键名缺少引号的问题
                        arguments_str = re.sub(r'(\w+):', r'"\1":', arguments_str)
                        arguments = json.loads(arguments_str)
                    except:
                        # 如果JSON解析失败，给出明确的错误提示
                        error_response = f"抱歉，参数格式错误。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                        safe_set_history_response(error_response)
                        yield history, chunk_show
                        return
                
                # 检查必要参数是否存在
                if function_name == "get_stock_historical_data":
                    required_params = ['symbol', 'start_date', 'end_date']
                    missing_params = [param for param in required_params if param not in arguments]
                    
                    if missing_params:
                        print(f"缺少必要参数: {missing_params}")
                        error_response = f"抱歉，缺少必要参数: {missing_params}。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                        safe_set_history_response(error_response)
                        yield history, chunk_show
                        return
                
                if function_name in function_map and arguments:
                    function_result = function_map[function_name](**arguments)
                    
                    # 添加function call结果到消息历史
                    messages.append({
                        'role': 'assistant',
                        'content': None,
                        'tool_calls': [tool_call]
                    })
                    messages.append({
                        'role': 'tool',
                        'tool_call_id': tool_call['id'],
                        'content': function_result
                    })
                    
                    # 获取最终回复
                    final_completion = client.chat.completions.create(
                        model=model,
                        messages=messages,
                        temperature=temperature,
                        max_tokens=max_tokens,
                        stream=True
                    )
                    
                    final_response = ""
                    for final_chunk in final_completion:
                        if final_chunk.choices[0].delta.content:
                            final_response += final_chunk.choices[0].delta.content
                            safe_set_history_response(final_response)
                            yield history, chunk_show
                else:
                    if not function_name:
                        print("Function name is empty")
                        error_response = "抱歉，无法识别查询类型。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                    elif function_name not in function_map:
                        print(f"Function {function_name} 不存在")
                        error_response = f"抱歉，不支持的查询类型: {function_name}"
                    else:
                        print("参数为空")
                        error_response = "抱歉，无法解析参数。请尝试更明确的表述，例如：'查询600000.SH从20240101到20240131的历史数据'"
                    safe_set_history_response(error_response)
                    yield history, chunk_show
                    
            except Exception as e:
                print(f"Function call执行失败: {e}")
                error_response = f"抱歉，股票数据查询出现错误: {str(e)}"
                safe_set_history_response(error_response)
                yield history, chunk_show
    else:
        # 如果没有tool_calls，说明大模型直接回复了文本
        if assistant_response:
            # 如果大模型直接返回了参数而不是调用函数，尝试手动调用
            if '{"symbol"' in assistant_response and '"start_date"' in assistant_response and '"end_date"' in assistant_response:
                try:
                    # 尝试从回复中提取JSON参数
                    import re
                    json_match = re.search(r'\{[^}]+\}', assistant_response)
                    if json_match:
                        json_str = json_match.group(0)
                        arguments = json.loads(json_str)
                        
                        if "get_stock_historical_data" in function_map:
                            function_result = function_map["get_stock_historical_data"](**arguments)
                            
                            # 格式化回复
                            formatted_response = f"根据您的查询，以下是{arguments['symbol']}从{arguments['start_date']}到{arguments['end_date']}的历史数据：\n\n{function_result}"
                            safe_set_history_response(formatted_response)
                            yield history, chunk_show
                            return
                except Exception as e:
                    print(f"手动调用函数失败: {e}")
            
            # 如果无法处理，直接显示大模型的回复
            safe_set_history_response(assistant_response)
            yield history, chunk_show